/*
 * COMP6471 Project
 *  
 * This file is created by Yuan Tao (ewan.msn@gmail.com)
 * Licensed under GNU GPL v3
 * 
 * $Author: ewan.msn@gmail.com $
 * $Date: 2011-12-03 21:16:31 +0000 (Sat, 03 Dec 2011) $
 * $Rev: 68 $
 * $HeadURL: http://comp6471.googlecode.com/svn/Project6/src/ui/UIConsole.java $
 * 
 */
package ui;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Observable;
import java.util.Observer;

import utils.SysLogger;

import kklib.Cage;
import kklib.Layout;

public class UIConsole implements UIBasicInf, Observer {
	
	// command types:
	public static final int EXIT = 1;		// exit program
	public static final int SETV = 2;		// set values
	public static final int SETPV = 3;		// set possible values
	public static final int GETHINT = 4;		// get hint

	@Override
	public int showGreetings() {
		System.out.println("Hi, wellcome to COMP6471 Project6. v0.1");
		System.out.println("Developed by Yuan Tao (ewan.msn@gmail.com)\n");
		System.out.println("Please read the design document first.\n");
		System.out.println("The game files are under the root directoy of $ThisProgram\\input\\");
		System.out.println("There are already some sample txt files under $input directory.");
		System.out.println("Then press any key to begin...");
		
		try {
			InputStreamReader isr = new InputStreamReader(System.in);
			BufferedReader br = new BufferedReader(isr);
			
			br.readLine();
		} catch (IOException e) {
			SysLogger.err("in.read: " + e.getMessage());
			return -1;
		}
		return 0;
	}
	
	// generate formula string used to show on UI
	private String getFormula(Cage c) {
		String formula = "" + c.targetNumber;
		
		switch (c.operator) {
		case Cage.OP_ADD:
			formula += '+';
			break;
		case Cage.OP_MUL:
			formula += '*';
			break;
		case Cage.OP_SUB:
			formula += '-';
			break;
		case Cage.OP_DIV:
			formula += '/';
			break;
		case Cage.OP_EQU:
			formula += '=';
			break;
		default:
			break;
		}
		return formula;
	}
	
	// check if the current cell is the first cell of a cage while drawing UI
	private boolean isFirstOfCage(Layout ly, int x, int y) {
//		int size = sb.length();
//		
//		if (sb.charAt(size - 1) == ' ') {
//			return false;
//		}
//		if (sb.charAt(size - ly.gameSize * 8) == ' ') {
//			return false;
//		}
		
		Cage c = ly.rows.get(ly.gameSize * x + y).cage;
		
		x++;
		y++;
		for (int i = 0; i < c.cells.size(); i++) {
			if ((x >= c.cells.get(i).y && y > c.cells.get(i).x) || (x > c.cells.get(i).y)) {
				return false;
			}
		}
		return true;
	}
	
	/*     1       2
	 * +-------+-------+
	   |24*            |         // formula
    A  |   8      2(3) |         // value of the cell, (3) is the hint
       |234     45     |         // the track of the cell values
       +       +-------+
       |       |5=     |
    B  |   2   |   5   |
       |       |       |
       +-------+-------+
	 */
	private void printOutGame(Layout layout) {
		StringBuffer sb = new StringBuffer();
		
		sb.append("Game Name: " + layout.gameName + "\n");
		
		// x coordinate
		sb.append("    ");
		for (int i = 0; i < layout.gameSize; i++) {
			sb.append("    " + (i + 1) + "   ");
		}
		
		// first line of layout
		sb.append("\n    +");
		for (int i = 0; i < layout.gameSize; i++) {
			sb.append("-------+");
		}
		sb.append("\n");
		
		// body of the layout
		for (int i = 0; i < layout.gameSize; i++) {
			int val = 0, data = 0, nextData = 0;
			
			// first line of cell
			sb.append("    |");
			for (int j = 0; j < layout.gameSize - 1; j++) {
				val = layout.rows.get(i * layout.gameSize + j).value;
				data = layout.rows.get(i * layout.gameSize + j).data;
				nextData = layout.rows.get(i * layout.gameSize + j + 1).data;
				
				if (!isFirstOfCage(layout, i, j)) {
					sb.append("       " + (data == nextData ? " " : "|"));
					continue;
				}
				
				// formula
				String formula = getFormula(layout.rows.get(i * layout.gameSize + j).cage);
				int cntBlanks = 7 - formula.length();
				for (int j2 = 0; j2 < cntBlanks; j2++) {
					formula += " ";
				}
				sb.append(formula + (data == nextData ? " " : "|"));
			}
			
			// first line of last cell
			if (!isFirstOfCage(layout, i, layout.gameSize - 1)) {
				sb.append("       |");
			} else {
				String formula = getFormula(layout.rows.get((i + 1) * layout.gameSize - 1).cage);
				int cntBlanks = 7 - formula.length();
				for (int j2 = 0; j2 < cntBlanks; j2++) {
					formula += " ";
				}
				sb.append(formula + "|");
			}
			sb.append("\n");
			
			// second line of the cell
			// y coordinate
			sb.append(" " + (char)('A' + i) + "  |");

			for (int j = 0; j < layout.gameSize - 1; j++) {
				val = layout.rows.get(i * layout.gameSize + j).value;
				data = layout.rows.get(i * layout.gameSize + j).data;
				nextData = layout.rows.get(i * layout.gameSize + j + 1).data;
				
				if (layout.rows.get(i * layout.gameSize + j).ifHint) {
					sb.append("  " + (val != 0 ? val : " ") 
							+ ("(" + layout.rows.get(i * layout.gameSize + j).rightVal + ")") 
							+ (data == nextData ? "  " : " |"));
				} else {
					sb.append("   " + (val != 0 ? val : " ") + (data == nextData ? "    " : "   |"));
				}
			}
			
			// second line of last cell
			val = layout.rows.get(i * layout.gameSize + layout.gameSize - 1).value;
			if (layout.rows.get(i * layout.gameSize + layout.gameSize - 1).ifHint) {
				sb.append("  " + (val != 0 ? val : " ") 
						+ ("(" + layout.rows.get(i * layout.gameSize + layout.gameSize - 1).rightVal + ")") 
						+ (" |"));
			} else {
				sb.append("   " + (val != 0 ? val : " ") + ("   |"));
			}
			sb.append("\n");
			//sb.append("   " + (val != 0 ? val : " ") + "   |\n");
			
			// third line of the cell
			sb.append("    |");
			for (int j = 0; j < layout.gameSize - 1; j++) {
				val = layout.rows.get(i * layout.gameSize + j).value;
				data = layout.rows.get(i * layout.gameSize + j).data;
				nextData = layout.rows.get(i * layout.gameSize + j + 1).data;
				
				String possValues = layout.rows.get(i * layout.gameSize + j).possValues;
				int cntBlanks = 7 - possValues.length();
				for (int j2 = 0; j2 < cntBlanks; j2++) {
					possValues += " ";
				}
				
				sb.append(possValues + (data == nextData ? " " : "|"));
			}
			
			// third line of last cell
			String possValues = layout.rows.get(i * layout.gameSize + layout.gameSize - 1).possValues;
			int cntBlanks = 7 - possValues.length();
			for (int j2 = 0; j2 < cntBlanks; j2++) {
				possValues += " ";
			}
			sb.append(possValues + "|\n");
			
			// forth line of the cell
			sb.append("    ");
			if (i != layout.gameSize - 1) {
				sb.append("+");
				for (int j = 0; j < layout.gameSize; j++) {
					data = layout.rows.get(i * layout.gameSize + j).data;
					nextData = layout.rows.get((i + 1) * layout.gameSize + j).data;
					
					sb.append(data == nextData ? "       +" : "-------+");
				}
				sb.append("\n");
			}
		}
		
		// last line of the layout
		sb.append("+");
		for (int k = 0; k < layout.gameSize; k++) {
			sb.append("-------+");
		}
		sb.append("\n");
		
		// print out
		System.out.println(sb.toString());
		
//		for (int j = 0; j < layout.txtFileFormulas.size(); j++) {
//			System.out.println(layout.txtFileFormulas.get(j));
//		}
	}
	
	private void printOutGames(ArrayList<Layout> layouts) {
		for (int i = 0; i < layouts.size(); i++) {
			printOutGame(layouts.get(i));
			System.out.println("");
		}
	}

	@Override
	public int showAllGamesAndChooseAGame(ArrayList<Layout> layouts) {
		// show all the games to user
		System.out.println("\n");
		printOutGames(layouts);
		
		String strSelection = "";
		// let user select a game
		for (int i = 0; i < layouts.size(); i++) {
			strSelection  += (i + 1) + ":" + layouts.get(i).gameName + " ";
		}
		System.out.println("Choose a game: \n" + strSelection + " OR exit");
		
		InputStreamReader isr = new InputStreamReader(System.in);
		BufferedReader br = new BufferedReader(isr);

		try {
			do {
				String line = br.readLine().trim();
				
				// if exit
				if (line.toLowerCase().compareToIgnoreCase("exit") == 0 ||
						line.toLowerCase().compareToIgnoreCase("quit") == 0) {
					return -1;
				}
				
				// get the game NO.
				int no = -1;
				try {
					no = Integer.parseInt(line);
					
					if (no < 1 || no > layouts.size()) {
						System.out.println("Wrong game NO., please input again.");
						continue;
					}
				} catch (NumberFormatException e) {
					System.out.println("Wrong game NO., please input again.");
					continue;
				}

				return no;
			} while (true);
		} catch (IOException e) {
			SysLogger.err("readline: " + e.getMessage());
			return -2;
		}
	}

	@Override
	public String getCommands() {
		// get input values of cells
		// parse the values from string like "a1:2" or "a1:2 b3:3 a2:4"
		InputStreamReader isr = new InputStreamReader(System.in);
		BufferedReader br = new BufferedReader(isr);
		String line;
		
		int cmd = 0;
		boolean ifLoop = false;
		do {
			System.out.println("1 Set values to cells. 2 Set possible values to cells. 3 Get hint. OR exit");

			try {
				System.out.flush();
				line = br.readLine().trim().toUpperCase();
			} catch (IOException e) {
				SysLogger.err(e.getMessage());
				return null;
			}

			// check if exit
			ifLoop = false;
			if (line.compareToIgnoreCase("exit") == 0 ||
					line.compareToIgnoreCase("quit") == 0) {
				cmd = EXIT;
				return cmd + "exit";
			
			} else if (line.compareToIgnoreCase("1") == 0) {
				cmd = SETV;
			} else if (line.compareToIgnoreCase("2") == 0) {
				cmd = SETPV;
			} else if (line.compareToIgnoreCase("3") == 0) {
				cmd = GETHINT;
			} else {
				ifLoop = true;
			}
		} while (ifLoop);

		if (cmd == SETV) {
			System.out.println("Please input the values of cells, e.g. a1=2 OR a1=2 b3=3 a2=4 OR exit");
		} else if (cmd == SETPV) {
			System.out.println("Please input the values of cells, e.g. a1=2 OR a1=2 b3=3 a2=4 OR a1=0 to clear data OR exit");
		} else {
			System.out.println("Please input the cells, e.g. a1 OR a1 b3 a2 OR exit");
		}
		try {
			System.out.flush();
			line = br.readLine().trim().toUpperCase();
		} catch (IOException e) {
			SysLogger.err(e.getMessage());
			return null;
		}
		
		if (line.compareToIgnoreCase("exit") == 0 ||
				line.compareToIgnoreCase("quit") == 0) {
			cmd = EXIT;
			return cmd + "exit";
		}		

		return cmd + line;
	}

	@Override
	public void showMessage(String msg) {
		System.out.println("\n");
		System.out.println(msg);
		SysLogger.info(msg);
	}

	@Override
	public boolean ifPlayAgain() {
		System.out.println("\nPlay it again? 'y' OR any key to exit");
		
		InputStreamReader isr = new InputStreamReader(System.in);
		BufferedReader br = new BufferedReader(isr);
		try {
			String line = br.readLine().trim().toLowerCase();
			
			if (line.compareToIgnoreCase("yes") == 0 ||
					line.compareToIgnoreCase("y") == 0) {
				return true;
			}
			
		} catch (IOException e) {
			SysLogger.err("readline: " + e.getMessage());
			return false;
		}
		return false;
	}

	@Override
	public void showLayout(Layout layout) {
		printOutGame(layout);		
	}

	
	// update UI when getting notification from Layout.
	// e.g. when Layout set a new value to cell
	@Override
	public void update(Observable o, Object arg) {
		printOutGame((Layout)arg);		
	}

}
